package com.hch.maker.template;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;

import com.hch.maker.meta.Meta;
import com.hch.maker.meta.enums.FIleGenerateTypeEnum;
import com.hch.maker.meta.enums.FIleTypeEnum;
import com.hch.maker.template.enums.FileFilterRangeEnum;
import com.hch.maker.template.enums.FileFilterRuleEnum;
import com.hch.maker.template.model.*;

import java.io.File;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 86134
 * @version 1.0
 * @description TODO
 * @date 2024/7/30 14:22
 */

public class TemplateMaker {




    public static Long TemplateMaker(TemplateMakerConfig templateMakerConfig){
        Meta meta = templateMakerConfig.getMeta();
        String originProjectPath = templateMakerConfig.getOriginProjectPath();
        TemplateMakerModelConfig templateMakerModelConfig = templateMakerConfig.getModelConfig();

        TemplateMakerFileConfig templateMakerFileConfig = templateMakerConfig.getFileConfig();
        TemplateMakerOutConfig templateMakerOutConfig = templateMakerConfig.getTemplateMakerOutConfig();
        Long id = templateMakerConfig.getId();
        return TemplateMaker(meta,originProjectPath,templateMakerFileConfig,templateMakerModelConfig,id,templateMakerOutConfig);
    }


    /**
     * 制作模板
     *
     * @param newMeta
     * @param originProjectPath
     * @param templateMakerFileConfig
     * @param templateMakerFileConfig
     * @param id
     * @param templateMakerOutConfig
     * @return
     */
    public static Long TemplateMaker(Meta newMeta, String originProjectPath, TemplateMakerFileConfig templateMakerFileConfig, TemplateMakerModelConfig templateMakerModelConfig, Long id, TemplateMakerOutConfig templateMakerOutConfig) {
        //第一次
        if (id == null) {
            //生成id
            id = IdUtil.getSnowflakeNextId();
        }
        //业务逻辑
        //实现工作空间隔离
        String RootPath = System.getProperty("user.dir");

        String tempPath = RootPath + File.separator + ".temp";
        String templatePath = tempPath + File.separator + id;
        if (!FileUtil.exist(templatePath)) {
            FileUtil.mkdir(templatePath);//复制文件
            FileUtil.copy(originProjectPath, templatePath, true);
        }

        ArrayList<Meta.ModelConfigDTO.ModelsDTO> newModelInfoList = getModelInfoList(templateMakerModelConfig);


        //1.项目的基本信息
        //获取模板文件
//        String sourceRootPath = templatePath + File.separator + FileUtil.getLastPathEle(Paths.get(originProjectPath)).toString();
        String sourceRootPath = FileUtil.loopFiles(new File(templatePath),1,null)
                .stream()
                .filter(File::isDirectory)
                .findFirst()
                .orElseThrow(RuntimeException::new)
                .getAbsolutePath();


        //注意: window环境下需要对斜杆做处理
        sourceRootPath = sourceRootPath.replaceAll("\\\\", "/");
        ArrayList<Meta.FileConfigDTO.FilesDTO>  newFileInfoList = makeFileTemplate(templateMakerFileConfig, templateMakerModelConfig, sourceRootPath);



        //1818264274695028736
        //生成配置文件
        String metaOutputPath = templatePath  + "/meta.json";
        if (FileUtil.exist(metaOutputPath)) {
            newMeta = JSONUtil.toBean(FileUtil.readUtf8String(metaOutputPath), Meta.class);
            //获取老配置加载配置参数
            List<Meta.FileConfigDTO.FilesDTO> files = newMeta.getFileConfig().getFiles();
            files.addAll(newFileInfoList);
            List<Meta.ModelConfigDTO.ModelsDTO> models = newMeta.getModelConfig().getModels();
            models.addAll(newModelInfoList);

            newMeta.getModelConfig().setModels(distinctModels(models));
            newMeta.getFileConfig().setFiles(distinctFiles(files));

            //输出原信息
            FileUtil.writeUtf8String(JSONUtil.toJsonPrettyStr(newMeta), metaOutputPath);
        } else {

            Meta.FileConfigDTO fileConfigDTO = new Meta.FileConfigDTO();

            fileConfigDTO.setSourceRootPath(sourceRootPath);
            ArrayList<Meta.FileConfigDTO.FilesDTO> list = new ArrayList<>();
            fileConfigDTO.setFiles(list);
            newMeta.setFileConfig(fileConfigDTO);
            list.addAll(newFileInfoList);

            Meta.ModelConfigDTO modelsDTO = new Meta.ModelConfigDTO();
            newMeta.setModelConfig(modelsDTO);
            ArrayList<Meta.ModelConfigDTO.ModelsDTO> modelList = new ArrayList<>();
            modelList.addAll(newModelInfoList);
            modelsDTO.setModels(modelList);

        }

        //额外输出配置
        if(templateMakerOutConfig != null){
            if(templateMakerOutConfig.isRemoveGroupFileFromRoot()){
                List<Meta.FileConfigDTO.FilesDTO> files = newMeta.getFileConfig().getFiles();
                newMeta.getFileConfig().setFiles(TemplateMakerUtils.removeGroupFilesFromRoot(files));
            }
        }
        //输出元信息
        FileUtil.writeUtf8String(JSONUtil.toJsonPrettyStr(newMeta), metaOutputPath);

        return id;
    }

    /**
     * 获取模型配置
     * @param templateMakerModelConfig
     * @return
     */
    private static ArrayList<Meta.ModelConfigDTO.ModelsDTO> getModelInfoList(TemplateMakerModelConfig templateMakerModelConfig) {
        //新模型
        ArrayList<Meta.ModelConfigDTO.ModelsDTO> newModelInfoList = new ArrayList<>();
        if(templateMakerModelConfig == null){
            return newModelInfoList;
        }
        //处理模型信息
        List<TemplateMakerModelConfig.ModelInfoConfig> modelInfoConfigList = templateMakerModelConfig.getModels();
        if(CollUtil.isEmpty(modelInfoConfigList)){
            return newModelInfoList;
        }

        //转换成modelInfo对象
        List<Meta.ModelConfigDTO.ModelsDTO> inputModelInfoList = modelInfoConfigList.stream().map(modelInfo -> {
            Meta.ModelConfigDTO.ModelsDTO modelsDTO = new Meta.ModelConfigDTO.ModelsDTO();
            BeanUtil.copyProperties(modelInfo, modelsDTO);
            return modelsDTO;
        }).collect(Collectors.toList());


        //如果是模型组
        TemplateMakerModelConfig.ModelGroupConfig modelsGroupConfig = templateMakerModelConfig.getModelsGroupConfig();
        if(modelsGroupConfig != null){
            String condition = modelsGroupConfig.getCondition();
            String groupKey = modelsGroupConfig.getGroupKey();
            String groupName = modelsGroupConfig.getGroupName();

            Meta.ModelConfigDTO.ModelsDTO groupModelInfo = new Meta.ModelConfigDTO.ModelsDTO();
            groupModelInfo.setCondition(condition);
            groupModelInfo.setGroupKey(groupKey);
            groupModelInfo.setGroupName(groupName);
            //文件全放到一个分组内
            groupModelInfo.setModels(inputModelInfoList);
            newModelInfoList = new ArrayList<>();
            newModelInfoList.add(groupModelInfo);
        }else{
            inputModelInfoList.addAll(newModelInfoList);
        }
        return newModelInfoList;
    }

    /**
     * 生成多个文件
     * @param templateMakerFileConfig
     * @param templateMakerModelConfig
     * @param sourceRootPath
     * @return
     */
    private static ArrayList<Meta.FileConfigDTO.FilesDTO> makeFileTemplate(TemplateMakerFileConfig templateMakerFileConfig, TemplateMakerModelConfig templateMakerModelConfig, String sourceRootPath) {
        ArrayList<Meta.FileConfigDTO.FilesDTO> newFileInfoList = new ArrayList<>();
        //非空校验
        if(templateMakerFileConfig == null){
            return newFileInfoList;
        }

        List<TemplateMakerFileConfig.FileInfoConfig> fileInfoConfigList = templateMakerFileConfig.getFiles();
        if (CollUtil.isEmpty(fileInfoConfigList)) {
            return newFileInfoList;
        }

        //遍历过滤配置类信息
        for (TemplateMakerFileConfig.FileInfoConfig fileInfoConfigBean : fileInfoConfigList) {
            String inputFileAbsolutePath = sourceRootPath + File.separator + fileInfoConfigBean.getPath();
            //获取到过滤文件列表
            List<File> fileList = FileFilter.doFilter(inputFileAbsolutePath, fileInfoConfigBean.getFileFilterConfigs());
            fileList = fileList.stream()
                    .filter(fileInfo -> !fileInfo.getAbsolutePath().endsWith(".ftl")).collect(Collectors.toList());
            //遍历文件列表
            for (File file : fileList) {
                //制作模板
                Meta.FileConfigDTO.FilesDTO fileInfo = makeFileTemplate(file, templateMakerModelConfig, sourceRootPath,templateMakerFileConfig);
                //添加信息
                newFileInfoList.add(fileInfo);
            }
        }

        //如果是文件组
        TemplateMakerFileConfig.FileGroupConfig filesGroupConfig = templateMakerFileConfig.getFilesGroupConfig();
        if(filesGroupConfig != null){
            String condition = filesGroupConfig.getCondition();
            String groupKey = filesGroupConfig.getGroupKey();
            String groupName = filesGroupConfig.getGroupName();

            Meta.FileConfigDTO.FilesDTO groupFileInfo = new Meta.FileConfigDTO.FilesDTO();
            groupFileInfo.setCondition(condition);
            groupFileInfo.setGroupKey(groupKey);
            groupFileInfo.setGroupName(groupName);
            //文件全放到一个分组内
            groupFileInfo.setFiles(newFileInfoList);
            newFileInfoList = new ArrayList<>();
            newFileInfoList.add(groupFileInfo);
        }
        return newFileInfoList;
    }

    /**
     * 文件去重
     *
     * @param fileInfoList
     * @return
     */
//    private static List<Meta.FileConfigDTO.FilesDTO> distinctFiles(List<Meta.FileConfigDTO.FilesDTO> fileInfoList) {
//        // 策略：同分组内文件 merge，不同分组保留
//
//        // 1. 有分组的，以组为单位划分
//        Map<String, List<Meta.FileConfigDTO.FilesDTO>> groupKeyFileInfoListMap = fileInfoList
//                .stream()
//                .filter(fileInfo -> StrUtil.isNotBlank(fileInfo.getGroupKey()))
//                .collect(
//                        Collectors.groupingBy(Meta.FileConfigDTO.FilesDTO::getGroupKey)
//                );
//
//
//        // 2. 同组内的文件配置合并
//        // 保存每个组对应的合并后的对象 map
//        Map<String, Meta.FileConfigDTO.FilesDTO> groupKeyMergedFileInfoMap = new HashMap<>();
//        for (Map.Entry<String, List<Meta.FileConfigDTO.FilesDTO>> entry : groupKeyFileInfoListMap.entrySet()) {
//            List<Meta.FileConfigDTO.FilesDTO> tempFileInfoList = entry.getValue();
//            List<Meta.FileConfigDTO.FilesDTO> newFileInfoList = new ArrayList<>(tempFileInfoList.stream()
//                    .flatMap(fileInfo -> fileInfo.getFiles().stream())
//                    .collect(
//                            Collectors.toMap(Meta.FileConfigDTO.FilesDTO::getInputPath, o -> o, (e, r) -> r)
//                    ).values());
//
//            // 使用新的 group 配置
//            Meta.FileConfigDTO.FilesDTO newFileInfo = CollUtil.getLast(tempFileInfoList);
//            newFileInfo.setFiles(newFileInfoList);
//            String groupKey = entry.getKey();
//            groupKeyMergedFileInfoMap.put(groupKey, newFileInfo);
//        }
//
//        // 3. 将文件分组添加到结果列表
//        List<Meta.FileConfigDTO.FilesDTO> resultList = new ArrayList<>(groupKeyMergedFileInfoMap.values());
//
//        // 4. 将未分组的文件添加到结果列表
//        List<Meta.FileConfigDTO.FilesDTO> noGroupFileInfoList = fileInfoList.stream().filter(fileInfo -> StrUtil.isBlank(fileInfo.getGroupKey()))
//                .collect(Collectors.toList());
//        resultList.addAll(new ArrayList<>(noGroupFileInfoList.stream()
//                .collect(
//                        Collectors.toMap(Meta.FileConfigDTO.FilesDTO::getInputPath, o -> o, (e, r) -> r)
//                ).values()));
//        return resultList;
//    }

    private static List<Meta.FileConfigDTO.FilesDTO> distinctFiles(List<Meta.FileConfigDTO.FilesDTO> fileInfoList){
        //1.将所有文件配置分为有组和无组
        Map<String, List<Meta.FileConfigDTO.FilesDTO>> groupKeyFileInfoList = fileInfoList.stream().filter(fileInfo -> StrUtil.isNotBlank(fileInfo.getGroupKey()))
                .collect(Collectors.groupingBy(Meta.FileConfigDTO.FilesDTO::getGroupKey));

        Map<String,Meta.FileConfigDTO.FilesDTO> groupKeyMergedFileInfoMap = new HashMap<>();

        //2.对分组文件配置,如果有相同组的则合并,不同分组的则保留
        for (Map.Entry<String, List<Meta.FileConfigDTO.FilesDTO>> entry : groupKeyFileInfoList.entrySet()) {
            List<Meta.FileConfigDTO.FilesDTO> templateFileList = entry.getValue();

            List<Meta.FileConfigDTO.FilesDTO> newFileInfoList = new ArrayList<>(templateFileList.stream()
                    .flatMap(fileInfo -> fileInfo.getFiles().stream())
                    .collect(Collectors.toMap(Meta.FileConfigDTO.FilesDTO::getOutputPath,o->o,(e,r)->r)).values());
            Meta.FileConfigDTO.FilesDTO newFileInfo = CollUtil.getLast(templateFileList);
            newFileInfo.setFiles(newFileInfoList);
            String groupKey = newFileInfo.getGroupKey();
            groupKeyMergedFileInfoMap.put(groupKey,newFileInfo);
        }
        //同组的配置信息的覆盖 使用新的group配置

        ArrayList<Meta.FileConfigDTO.FilesDTO> resultList = new ArrayList<>(groupKeyMergedFileInfoMap.values());

        resultList.addAll(new ArrayList<>(fileInfoList.stream()
                .filter(fileInfo -> StrUtil.isBlank(fileInfo.getGroupKey()))
                .collect(Collectors.toMap(Meta.FileConfigDTO.FilesDTO::getOutputPath,o->o,(e,r)->r)).values()));

        return resultList;

    }

    private static Meta.FileConfigDTO.FilesDTO makeFileTemplate(File inputFile, TemplateMakerModelConfig templateMakerModelConfig, String sourceRootPath,TemplateMakerFileConfig fileInfoConfig) {
        //生成的文件
        String fileInputPath = inputFile.getAbsolutePath();
        fileInputPath = fileInputPath.replace("\\","/");
        fileInputPath = fileInputPath.replace(sourceRootPath + "/", "");
        String fileOutputPath = fileInputPath + ".ftl";

        //封装原信息
        Meta.FileConfigDTO.FilesDTO fileInfo = new Meta.FileConfigDTO.FilesDTO();
        fileInfo.setInputPath(fileOutputPath);
        fileInfo.setOutputPath(fileInputPath);
        fileInfo.setCondition(fileInfoConfig.getFilesGroupConfig().getCondition());
        fileInfo.setType(FIleTypeEnum.FILE.getValue());
        fileInfo.setGenerateType(FIleGenerateTypeEnum.DYNAMIC.getValue());

        //使用字符串替换
        String fileInputAbsolutePath = inputFile.getAbsolutePath();
        String fileOutputAbsolutePath = inputFile.getAbsolutePath() + ".ftl";

        String fileContent;
        //如果已经上传过一次了那就在上次的基础上继续挖坑
        boolean hasTemplateFile = FileUtil.exist(fileOutputAbsolutePath);
        if (hasTemplateFile) {
            fileContent = FileUtil.readUtf8String(fileOutputAbsolutePath);
        } else {
            fileContent = FileUtil.readUtf8String(fileInputAbsolutePath);
        }

        //支持多模型字符串替换
        TemplateMakerModelConfig.ModelGroupConfig modelsGroupConfig = templateMakerModelConfig.getModelsGroupConfig();

        String newFileContent = fileContent;
        String replacement;
        for (TemplateMakerModelConfig.ModelInfoConfig modelInfoConfig : templateMakerModelConfig.getModels()) {
            String fieldName = modelInfoConfig.getFieldName();
            //是否是分组
            if(modelInfoConfig != null){
                replacement = String.format("${%s}", fieldName);
            }else{
                //分组
                String groupKey = modelsGroupConfig.getGroupKey();
                replacement = String.format("${%s.%s}",groupKey ,fieldName);
            }
            newFileContent = StrUtil.replace(newFileContent,modelInfoConfig.getReplaceText(),replacement);
        }

        boolean contentEquals = newFileContent.equals(fileContent);

        if(!hasTemplateFile){
            if(contentEquals){
                //没有被挖坑
                fileInfo.setInputPath(fileInputPath);
                fileInfo.setGenerateType(FIleGenerateTypeEnum.STATIC.getValue());
            }else{
                fileInfo.setGenerateType(FIleGenerateTypeEnum.DYNAMIC.getValue());
                //输出模板文件
                FileUtil.writeUtf8String(newFileContent, fileOutputAbsolutePath);
            }
        }else if(!contentEquals){
            FileUtil.writeUtf8String(newFileContent, fileOutputAbsolutePath);
        }




        return fileInfo;
    }

    public static void main(String[] args) {
        //1.项目的基本信息
        //封装meta元信息
        Meta meta = new Meta();
        meta.setName("springboot-init-master");
        meta.setDescription("ACM 示例文件");
        String RootPath = System.getProperty("user.dir");
        String originProjectPath = new File(RootPath).getParent() + File.separator + "hch-generator-demo-projects/springboot-init-master";
        //要挖坑的文件
        String fileInputPath2 = "src/main/java/com/yupi/springbootinit/common";
        ArrayList<String> list = new ArrayList<>();
        String fileInputPath = "src/main/java/com/yupi/springbootinit/config";
        list.add(fileInputPath);
        list.add(fileInputPath2);
        Meta.ModelConfigDTO.ModelsDTO modelInfo = new Meta.ModelConfigDTO.ModelsDTO();
        modelInfo.setFieldName("outputText");
        modelInfo.setType("String");
        String searchStr = "BaseResponse";

        TemplateMakerFileConfig templateMakerFileConfig = new TemplateMakerFileConfig();
        List<TemplateMakerFileConfig.FileInfoConfig> list1 = new ArrayList<>();

        TemplateMakerFileConfig.FileInfoConfig fileInfoConfig = new TemplateMakerFileConfig.FileInfoConfig();
        fileInfoConfig.setPath(fileInputPath2);
        FileFilterConfig fileFilterConfig = new FileFilterConfig();
        fileFilterConfig.setRange(FileFilterRangeEnum.FILE_NAME.getValue());
        fileFilterConfig.setRule(FileFilterRuleEnum.CONTAINS.getValue());
        fileFilterConfig.setValue("Base");
        fileInfoConfig.setFileFilterConfigs(Arrays.asList(fileFilterConfig));
        list1.add(fileInfoConfig);
        templateMakerFileConfig.setFiles(list1);

        TemplateMakerFileConfig.FileGroupConfig fileGroupConfig = new TemplateMakerFileConfig.FileGroupConfig();
        fileGroupConfig.setCondition("output");
        fileGroupConfig.setGroupKey("test");
        fileGroupConfig.setGroupName("测试分组");

        templateMakerFileConfig.setFilesGroupConfig(fileGroupConfig);

        TemplateMakerModelConfig.ModelInfoConfig modelInfoConfig = new TemplateMakerModelConfig.ModelInfoConfig();
        modelInfoConfig.setFieldName("url");
        modelInfoConfig.setType("String");
        modelInfoConfig.setDefaultValue("root");
        modelInfoConfig.setReplaceText("root");

        TemplateMakerModelConfig.ModelInfoConfig modelInfoConfig2 = new TemplateMakerModelConfig.ModelInfoConfig();
        modelInfoConfig2.setFieldName("username");
        modelInfoConfig2.setType("String");
        modelInfoConfig2.setDefaultValue("root");
        modelInfoConfig2.setReplaceText("root");

        List<TemplateMakerModelConfig.ModelInfoConfig> modelInfoConfigList = Arrays.asList(modelInfoConfig, modelInfoConfig2);
        TemplateMakerModelConfig templateMakerModelConfig = new TemplateMakerModelConfig();
        templateMakerModelConfig.setModels(modelInfoConfigList);

        TemplateMakerModelConfig.ModelGroupConfig modelGroupConfig = new TemplateMakerModelConfig.ModelGroupConfig();
        modelGroupConfig.setGroupKey("mysql");
        modelGroupConfig.setGroupName("数据库配置");

        templateMakerModelConfig.setModelsGroupConfig(modelGroupConfig);


//        modelInfo.setFieldName("className");
//        modelInfo.setType("String");
//        String searchStr = "MainTemplate";
        TemplateMaker(meta, originProjectPath, templateMakerFileConfig, templateMakerModelConfig, 1L,new TemplateMakerOutConfig());

    }

    /**
     * 模型去重
     *
     * @param modelInfoList
     * @return
     */
    public static List<Meta.ModelConfigDTO.ModelsDTO> distinctModels(List<Meta.ModelConfigDTO.ModelsDTO> modelInfoList) {
        //1.将所有模型配置分为有组和无组
        Map<String, List<Meta.ModelConfigDTO.ModelsDTO>> groupKeyModelInfoList = modelInfoList.stream().filter(modelInfo -> StrUtil.isNotBlank(modelInfo.getGroupKey()))
                .collect(Collectors.groupingBy(Meta.ModelConfigDTO.ModelsDTO::getGroupKey));

        Map<String,Meta.ModelConfigDTO.ModelsDTO> groupKeyMergedModelInfoMap = new HashMap<>();

        //2.对分组模型配置,如果有相同组的则合并,不同分组的则保留
        for (Map.Entry<String, List<Meta.ModelConfigDTO.ModelsDTO>> entry : groupKeyModelInfoList.entrySet()) {
            List<Meta.ModelConfigDTO.ModelsDTO> templateModelList = entry.getValue();

            List<Meta.ModelConfigDTO.ModelsDTO> newModelInfoList = new ArrayList<>(templateModelList.stream()
                    .flatMap(modelInfo -> modelInfo.getModels().stream())
                    .collect(Collectors.toMap(Meta.ModelConfigDTO.ModelsDTO::getFieldName,o->o,(e,r)->r)).values());
            Meta.ModelConfigDTO.ModelsDTO newModelInfo = CollUtil.getLast(templateModelList);
            newModelInfo.setModels(newModelInfoList);
            String groupKey = newModelInfo.getGroupKey();
            groupKeyMergedModelInfoMap.put(groupKey,newModelInfo);
        }
        //同组的配置信息的覆盖 使用新的group配置

        ArrayList<Meta.ModelConfigDTO.ModelsDTO> resultList = new ArrayList<>(groupKeyMergedModelInfoMap.values());

        resultList.addAll(new ArrayList<>(modelInfoList.stream()
                .filter(modelInfo -> StrUtil.isBlank(modelInfo.getGroupKey()))
                .collect(Collectors.toMap(Meta.ModelConfigDTO.ModelsDTO::getFieldName,o->o,(e,r)->r)).values()));

        return resultList;
    }



//    /**
//     * 文件去重
//     *
//     * @param fileInfoList
//     * @return
//     */
//    public static List<Meta.FileConfigDTO.FilesDTO> distinctFiles(List<Meta.FileConfigDTO.FilesDTO> fileInfoList) {
//        ArrayList<Meta.FileConfigDTO.FilesDTO> newFileInfoList = new ArrayList<>(fileInfoList.stream().collect(Collectors.toMap(Meta.FileConfigDTO.FilesDTO::getInputPath, o -> o, (e, r) -> r)).values());
//        return newFileInfoList;
//    }
}
